`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    13:21:10 11/30/2011 
// Design Name: 
// Module Name:    ConfigurationSpace 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////

module ConfigurationSpace #(
  parameter       BAR0 = 32'h00000000,
  parameter       BAR1 = 32'h00000000,
  parameter       BAR2 = 32'h00000000,
  parameter       BAR3 = 32'h00000000,
  parameter       BAR4 = 32'h00000000,
  parameter       BAR5 = 32'h00000000,
  parameter       CAPABILITIES_PTR = 8'h40,
  parameter       CARDBUS_CIS_POINTER = 32'h00000000,
  parameter       CLASS_CODE = 24'h050000,
  parameter       DEVICE_ID = 16'h6024,
  parameter       DEVICE_TYPE = 4'h0,
  parameter       HEADER_TYPE = 8'h00,
  parameter       INTERRUPT_PIN = 8'h1,
  parameter       IO_DECODE_TYPE = 4'h1,
  parameter       PREFETCHABLE_DECODE_TYPE = 4'h1,
  parameter       REVISION_ID = 8'h00,
  parameter       SUBSYSTEM_ID = 16'h0007,
  parameter       SUBSYSTEM_VENDOR_ID = 16'h10EE,
  parameter       VENDOR_ID = 16'h10EE
)(
  input           clk,
  input           rst,

  input           drp_den,
  input           drp_dwe,
  input   [8:0]   drp_daddr,
  input   [3:0]   drp_dwbe,
  input   [31:0]  drp_dwdata,
  output          drp_drdy,
  output  [31:0]  drp_drdata,

  output          pcie_drp_clk,
  output          pcie_drp_den,
  output          pcie_drp_dwe,
  output  [8:0]   pcie_drp_daddr,
  output  [15:0]  pcie_drp_dwdata,
  input           pcie_drp_drdy,
  input   [15:0]  pcie_drp_drdata,

  input  [31:0]   di,
  output reg [31:0]   do,
  input  [3:0]    byte_en,
  output reg      rd_wr_done,
  input  [9:0]    dwaddr,
  input           rd_en,
  input           wr_en,
  
  input  [15:0]   Status,
  output [15:0]   Command,
  output [31:0]   BaseAddressRegister0,
  output [31:0]   BaseAddressRegister1,
  output [31:0]   BaseAddressRegister2,
  output [31:0]   BaseAddressRegister3,
  output [31:0]   BaseAddressRegister4,
  output [31:0]   BaseAddressRegister5,
  output [7:0]    PrimaryBusNumber,
  output [7:0]    SecondaryBusNumber,
  output [7:0]    SubordinateBusNumber,
  output [31:12]  IOBase,
  output [31:12]  IOLimit,
  output [31:20]  MemoryBase,
  output [31:20]  MemoryLimit,
  output [63:20]  PrefetchableMemoryBase,
  output [63:20]  PrefetchableMemoryLimit,
  output [31:0]   ExpansionROMBaseAddress,
  output [15:0]   BridgeControl

  );

  reg [9:0]   r_dwaddr = 0;
  reg [31:0]  r_di = 0;
  reg [3:0]   r_byte_en = 0;
  wire [31:0] hdr_do;
  wire [31:0] pci_do;
  wire [31:0] pcie_ex_do;
  reg         hdr_rd_en = 0;
  reg         hdr_wr_en = 0;
  reg         pci_rd_en = 0;
  reg         pci_wr_en = 0;
  reg         pcie_ex_rd_en = 0;
  reg         pcie_ex_wr_en = 0;
  wire        hdr_rd_wr_done;
  wire        pci_rd_wr_done;
  wire        pcie_ex_rd_wr_done;
  
  always @*
  begin
    do <= 32'b0;
    rd_wr_done <= 1'b0;
    if (r_dwaddr[9:4] == 0)
    begin
      do <= hdr_do;
      rd_wr_done <= hdr_rd_wr_done;
    end
    else if (r_dwaddr[9:6] == 0)
    begin
      do <= pci_do;
      rd_wr_done <= pci_rd_wr_done;
    end
    else
    begin
      do <= pcie_ex_do;
      rd_wr_done <= pcie_ex_rd_wr_done;
    end
  end
  
  always @(posedge clk)
  begin
    if (rd_en ||| wr_en)
      r_dwaddr <= dwaddr;
    if (wr_en)
    begin
      r_di <= di;
      r_byte_en <= byte_en;
    end
    if (dwaddr[9:4] == 0)
    begin
      if (hdr_rd_wr_done)
      begin
        hdr_rd_en <= 1'b0;
        hdr_wr_en <= 1'b0;
      end
      else 
      begin
        hdr_rd_en <= rd_en;
        hdr_wr_en <= wr_en;
      end 
    end  
    else if (dwaddr[9:6] == 0)
    begin
      if (pci_rd_wr_done)
      begin
        pci_rd_en <= 1'b0;
        pci_wr_en <= 1'b0;
      end
      else
      begin
        pci_rd_en <= rd_en;
        pci_wr_en <= wr_en;
      end
    end
    else
    begin
      if (pcie_ex_rd_wr_done)
      begin
        pcie_ex_rd_en <= 1'b0;
        pcie_ex_wr_en <= 1'b0;
      end
      else
      begin
        pcie_ex_rd_en <= rd_en;
        pcie_ex_wr_en <= wr_en;
      end
    end  
  end
  
  generate
    if (HEADER_TYPE == 8'h0)
    begin : Type0Header
      Type0ConfigurationSpaceHeader #(
        .BAR0                 (BAR0),
        .BAR1                 (BAR1),
        .BAR2                 (BAR2),
        .BAR3                 (BAR3),
        .BAR4                 (BAR4),
        .BAR5                 (BAR5),
        .CAPABILITIES_PTR     (CAPABILITIES_PTR),
        .CARDBUS_CIS_POINTER  (CARDBUS_CIS_POINTER),
        .CLASS_CODE           (CLASS_CODE),
        .DEVICE_ID            (DEVICE_ID),
        .INTERRUPT_PIN        (INTERRUPT_PIN),
        .REVISION_ID          (REVISION_ID),
        .SUBSYSTEM_ID         (SUBSYSTEM_ID),
        .SUBSYSTEM_VENDOR_ID  (SUBSYSTEM_VENDOR_ID),
        .VENDOR_ID            (VENDOR_ID))
      type0_cs_hdr (
        .clk                    (clk),
        .rst                    (rst),
      
        .di                     (r_di),
        .do                     (hdr_do),
        .byte_en                (r_byte_en),
        .rd_wr_done             (hdr_rd_wr_done),
        .dwaddr                 (r_dwaddr),
        .rd_en                  (hdr_rd_en),
        .wr_en                  (hdr_wr_en),
      
        .Status                 (Status),
        .Command                (Command),
        .BaseAddressRegister0   (BaseAddressRegister0),
        .BaseAddressRegister1   (BaseAddressRegister1),
        .BaseAddressRegister2   (BaseAddressRegister2),
        .BaseAddressRegister3   (BaseAddressRegister3),
        .BaseAddressRegister4   (BaseAddressRegister4),
        .BaseAddressRegister5   (BaseAddressRegister5),
        .ExpansionROMBaseAddress(ExpansionROMBaseAddress)
      );
    end
    else if (HEADER_TYPE == 8'h1)
    begin : Type1Header
      Type1ConfigurationSpaceHeader #(
        .BAR0                 (BAR0),
        .BAR1                 (BAR1),
        .CAPABILITIES_PTR     (CAPABILITIES_PTR),
        .CLASS_CODE           (CLASS_CODE),
        .DEVICE_ID            (DEVICE_ID),
        .INTERRUPT_PIN        (INTERRUPT_PIN),
        .IO_DECODE_TYPE       (IO_DECODE_TYPE),
        .PREFETCHABLE_DECODE_TYPE(PREFETCHABLE_DECODE_TYPE),
        .REVISION_ID          (REVISION_ID),
        .VENDOR_ID            (VENDOR_ID))
      type1_cs_hdr (
        .clk                    (clk),
        .rst                    (rst),
      
        .di                     (r_di),
        .do                     (hdr_do),
        .byte_en                (r_byte_en),
        .rd_wr_done             (hdr_rd_wr_done),
        .dwaddr                 (r_dwaddr),
        .rd_en                  (hdr_rd_en),
        .wr_en                  (hdr_wr_en),
      
        .Status                 (Status),
        .Command                (Command),
        .BaseAddressRegister0   (BaseAddressRegister0),
        .BaseAddressRegister1   (BaseAddressRegister1),
        .PrimaryBusNumber       (PrimaryBusNumber),
        .SecondaryBusNumber     (SecondaryBusNumber),
        .SubordinateBusNumber   (SubordinateBusNumber),
        .IOBase                 (IOBase),
        .IOLimit                (IOLimit),
        .MemoryBase             (MemoryBase),
        .MemoryLimit            (MemoryLimit),
        .PrefetchableMemoryBase (PrefetchableMemoryBase),
        .PrefetchableMemoryLimit(PrefetchableMemoryLimit),
        .ExpansionROMBaseAddress(ExpansionROMBaseAddress),
        .BridgeControl          (BridgeControl)
      );
    end
  endgenerate

      PCIConfigurationSpace #(
        .DEVICE_TYPE (DEVICE_TYPE))
      pci_cs(
            .clk                    (clk),
            .rst                    (rst),
          
            .di                     (r_di),
            .do                     (pci_do),
            .byte_en                (r_byte_en),
            .rd_wr_done             (pci_rd_wr_done),
            .dwaddr                 (r_dwaddr),
            .rd_en                  (pci_rd_en),
            .wr_en                  (pci_wr_en)
          
      );

      PCIeExtendedConfigurationSpace 
      pcie_ex_cs(
            .clk                    (clk),
            .rst                    (rst),
          
            .di                     (r_di),
            .do                     (pcie_ex_do),
            .byte_en                (r_byte_en),
            .rd_wr_done             (pcie_ex_rd_wr_done),
            .dwaddr                 (r_dwaddr),
            .rd_en                  (pcie_ex_rd_en),
            .wr_en                  (pcie_ex_wr_en)
          
      );
endmodule

module Type0ConfigurationSpaceHeader #(
  parameter       BAR0 = 32'hFFFFFF80,
  parameter       BAR1 = 32'h00000000,
  parameter       BAR2 = 32'hFFFFFF80,
  parameter       BAR3 = 32'h00000000,
  parameter       BAR4 = 32'h00000000,
  parameter       BAR5 = 32'h00000000,
  parameter       CAPABILITIES_PTR = 8'h40,
  parameter       CARDBUS_CIS_POINTER = 32'h00000000,
  parameter       CLASS_CODE = 24'h050000,
  parameter       DEVICE_ID = 16'h6024,
  parameter       INTERRUPT_PIN = 8'h1,
  parameter       REVISION_ID = 8'h00,
  parameter       SUBSYSTEM_ID = 16'h0007,
  parameter       SUBSYSTEM_VENDOR_ID = 16'h10EE,
  parameter       VENDOR_ID = 16'h10EE
)(
  input               clk,
  input               rst,

  input   [31:0]      di,
  output reg [31:0]   do,
  input   [3:0]       byte_en,
  output reg          rd_wr_done,
  input   [3:0]       dwaddr,
  input               rd_en,
  input               wr_en,

  input      [15:0]   Status,
  output reg [15:0]   Command = 0,
  output reg [31:0]   BaseAddressRegister0 = 0,
  output reg [31:0]   BaseAddressRegister1 = 0,
  output reg [31:0]   BaseAddressRegister2 = 0,
  output reg [31:0]   BaseAddressRegister3 = 0,
  output reg [31:0]   BaseAddressRegister4 = 0,
  output reg [31:0]   BaseAddressRegister5 = 0,
  output reg [31:0]   ExpansionROMBaseAddress = 0
);

  reg [7:0] CacheLineSize = 0;
  reg [7:0] InterruptLine = 0;
  
  always @(posedge clk)
  begin
    case (dwaddr)
      4'h0: do <= {DEVICE_ID, VENDOR_ID};
      4'h1: do <= {Status, Command};
      4'h2: do <= {CLASS_CODE, REVISION_ID};
      4'h3: do <= {8'h00, 8'h00, 8'h00, CacheLineSize};
      4'h4: do <= BaseAddressRegister0;
      4'h5: do <= BaseAddressRegister1;
      4'h6: do <= BaseAddressRegister2;
      4'h7: do <= BaseAddressRegister3;
      4'h8: do <= BaseAddressRegister4;
      4'h9: do <= BaseAddressRegister5;
      4'hA: do <= CARDBUS_CIS_POINTER;
      4'hB: do <= {SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID};
      4'hC: do <= ExpansionROMBaseAddress;
      4'hD: do <= {24'h0, CAPABILITIES_PTR};
      4'hE: do <= 32'h0;
      4'hF: do <= {8'h0, 8'h0, INTERRUPT_PIN, InterruptLine};
    endcase
  end

  always @(posedge clk)
  begin
    if (wr_en)
      case (dwaddr)
        4'h1:
        begin
          if (byte_en[0]) Command[7:0] <= di[7:0];
          if (byte_en[1]) Command[15:8] <= di[15:8];
        end
        4'h3:
        begin
          if (byte_en[0]) CacheLineSize <= di[7:0];
        end
        4'h4:
        begin
          if (byte_en[0]) BaseAddressRegister0[ 7: 0] <= {di[ 7: 0] & BAR0[ 7: 0]};
          if (byte_en[1]) BaseAddressRegister0[15: 8] <= {di[15: 8] & BAR0[15: 8]};
          if (byte_en[2]) BaseAddressRegister0[23:16] <= {di[23:16] & BAR0[23:16]};
          if (byte_en[3]) BaseAddressRegister0[31:24] <= {di[31:24] & BAR0[31:24]};
        end
        4'h5:
        begin
          if (byte_en[0]) BaseAddressRegister1[ 7: 0] <= {di[ 7: 0] & BAR1[ 7: 0]};
          if (byte_en[1]) BaseAddressRegister1[15: 8] <= {di[15: 8] & BAR1[15: 8]};
          if (byte_en[2]) BaseAddressRegister1[23:16] <= {di[23:16] & BAR1[23:16]};
          if (byte_en[3]) BaseAddressRegister1[31:24] <= {di[31:24] & BAR1[31:24]};
        end
        4'h6:
        begin
          if (byte_en[0]) BaseAddressRegister2[ 7: 0] <= {di[ 7: 0] & BAR2[ 7: 0]};
          if (byte_en[1]) BaseAddressRegister2[15: 8] <= {di[15: 8] & BAR2[15: 8]};
          if (byte_en[2]) BaseAddressRegister2[23:16] <= {di[23:16] & BAR2[23:16]};
          if (byte_en[3]) BaseAddressRegister2[31:24] <= {di[31:24] & BAR2[31:24]};
        end
        4'h7:
        begin
          if (byte_en[0]) BaseAddressRegister3[ 7: 0] <= {di[ 7: 0] & BAR3[ 7: 0]};
          if (byte_en[1]) BaseAddressRegister3[15: 8] <= {di[15: 8] & BAR3[15: 8]};
          if (byte_en[2]) BaseAddressRegister3[23:16] <= {di[23:16] & BAR3[23:16]};
          if (byte_en[3]) BaseAddressRegister3[31:24] <= {di[31:24] & BAR3[31:24]};
        end
        4'h8:
        begin
          if (byte_en[0]) BaseAddressRegister4[ 7: 0] <= {di[ 7: 0] & BAR4[ 7: 0]};
          if (byte_en[1]) BaseAddressRegister4[15: 8] <= {di[15: 8] & BAR4[15: 8]};
          if (byte_en[2]) BaseAddressRegister4[23:16] <= {di[23:16] & BAR4[23:16]};
          if (byte_en[3]) BaseAddressRegister4[31:24] <= {di[31:24] & BAR4[31:24]};
        end
        4'h9:
        begin
          if (byte_en[0]) BaseAddressRegister5[ 7: 0] <= {di[ 7: 0] & BAR5[ 7: 0]};
          if (byte_en[1]) BaseAddressRegister5[15: 8] <= {di[15: 8] & BAR5[15: 8]};
          if (byte_en[2]) BaseAddressRegister5[23:16] <= {di[23:16] & BAR5[23:16]};
          if (byte_en[3]) BaseAddressRegister5[31:24] <= {di[31:24] & BAR5[31:24]};
        end
      endcase
  end

  always @(posedge clk)
  begin
    if (rd_wr_done)
      rd_wr_done <= 1'b0;
    else
      rd_wr_done <= rd_en || wr_en;
  end
  
endmodule

module Type1ConfigurationSpaceHeader #(
  parameter       BAR0 = 32'h00000000,
  parameter       BAR1 = 32'h00000000,
  parameter       CLASS_CODE = 24'h050000,
  parameter       CAPABILITIES_PTR = 8'h40,
  parameter       DEVICE_ID = 16'h6024,
  parameter       EXPANSION_ROM = 32'h00000000,
  parameter       INTERRUPT_PIN = 8'h1,
  parameter       IO_DECODE_TYPE = 4'h1,
  parameter       PREFETCHABLE_DECODE_TYPE = 4'h1,
  parameter       REVISION_ID = 8'h00,
  parameter       VENDOR_ID = 16'h10EE
)(
  input               clk,
  input               rst,

  input   [31:0]      di,
  output reg [31:0]   do,
  input   [3:0]       byte_en,
  output reg          rd_wr_done,
  input   [9:0]       dwaddr,
  input               rd_en,
  input               wr_en,

  input      [15:0]   Status,
  output reg [15:0]   Command               = 0,
  output reg [31:0]   BaseAddressRegister0  = 0,
  output reg [31:0]   BaseAddressRegister1  = 0,
  output reg [7:0]    PrimaryBusNumber      = 8'h00,
  output reg [7:0]    SecondaryBusNumber    = 8'h00,
  output reg [7:0]    SubordinateBusNumber  = 8'h00,
  output reg [31:12]  IOBase                   = 20'h00000,
  output reg [31:12]  IOLimit                  = 20'h00000,
  output reg [31:20]  MemoryBase               = 12'h000,
  output reg [31:20]  MemoryLimit              = 12'h000,
  output reg [63:20]  PrefetchableMemoryBase   = 44'h00000000000,
  output reg [63:20]  PrefetchableMemoryLimit  = 44'h00000000000,
  output reg [31:0]   ExpansionROMBaseAddress = 0, 
  output reg [15:0]   BridgeControl           = 0 
);

  reg [7:0] CacheLineSize = 0;
  reg [7:0] InterruptLine = 0;
  wire [15:0] SecondaryStatus = 0;
  
  always @(posedge clk)
  begin
    case (dwaddr)
      4'h0: do <= {DEVICE_ID, VENDOR_ID};
      4'h1: do <= {Status, Command};
      4'h2: do <= {CLASS_CODE, REVISION_ID};
      4'h3: do <= {8'h00, 8'h01, 8'h00, CacheLineSize};
      4'h4: do <= BaseAddressRegister0;
      4'h5: do <= BaseAddressRegister1;
      4'h6: do <= {8'h00, SubordinateBusNumber, SecondaryBusNumber, PrimaryBusNumber};
      4'h7: do <= {SecondaryStatus, IOLimit[15:12], IO_DECODE_TYPE, IOBase[15:12], IO_DECODE_TYPE};
      4'h8: do <= {MemoryLimit[31:20], 4'h0, MemoryBase[31:20], 4'h0};
      4'h9: do <= {PrefetchableMemoryLimit[31:20], PREFETCHABLE_DECODE_TYPE, PrefetchableMemoryBase[31:20], PREFETCHABLE_DECODE_TYPE};
      4'hA: do <= PrefetchableMemoryBase[63:32];
      4'hB: do <= PrefetchableMemoryLimit[63:32];
      4'hC: do <= {IOLimit[31:16], IOBase[31:16]};
      4'hD: do <= {24'h0, CAPABILITIES_PTR};
      4'hE: do <= ExpansionROMBaseAddress;
      4'hF: do <= {BridgeControl, INTERRUPT_PIN, InterruptLine};
    endcase
  end

  always @(posedge clk)
  begin
    if (wr_en)
      case (dwaddr)
        4'h1:
        begin
          if (byte_en[0]) Command[7:0]                    <= di[7:0];
          if (byte_en[1]) Command[15:8]                   <= di[15:8];
        end
        4'h3:
        begin
          if (byte_en[0]) CacheLineSize                   <= di[7:0];
        end
        4'h4:
        begin
          if (byte_en[0]) BaseAddressRegister0[ 7: 0] <= {di[ 7: 0] & BAR0[ 7: 0]};
          if (byte_en[1]) BaseAddressRegister0[15: 8] <= {di[15: 8] & BAR0[15: 8]};
          if (byte_en[2]) BaseAddressRegister0[23:16] <= {di[23:16] & BAR0[23:16]};
          if (byte_en[3]) BaseAddressRegister0[31:24] <= {di[31:24] & BAR0[31:24]};
        end
        4'h5:
        begin
          if (byte_en[0]) BaseAddressRegister1[ 7: 0] <= {di[ 7: 0] & BAR1[ 7: 0]};
          if (byte_en[1]) BaseAddressRegister1[15: 8] <= {di[15: 8] & BAR1[15: 8]};
          if (byte_en[2]) BaseAddressRegister1[23:16] <= {di[23:16] & BAR1[23:16]};
          if (byte_en[3]) BaseAddressRegister1[31:24] <= {di[31:24] & BAR1[31:24]};
        end
        4'h6:
        begin
          if (byte_en[0]) PrimaryBusNumber                <= di[7:0];
          if (byte_en[1]) SecondaryBusNumber              <= di[15:8];
          if (byte_en[2]) SubordinateBusNumber            <= di[23:16];
        end
        4'h7:
        begin
          if (byte_en[0]) IOBase[15:12]                   <= di[7:4];
          if (byte_en[1]) IOLimit[15:12]                  <= di[15:12];
        end
        4'h8:
        begin
          if (byte_en[0]) MemoryBase[23:20]               <= di[7:4];
          if (byte_en[1]) MemoryBase[31:24]               <= di[15:8];
          if (byte_en[2]) MemoryLimit[23:20]              <= di[23:20];
          if (byte_en[3]) MemoryLimit[31:24]              <= di[31:24];
        end
        4'h9:
        begin
          if (byte_en[0]) PrefetchableMemoryBase[23:20]   <= di[7:4];
          if (byte_en[1]) PrefetchableMemoryBase[31:24]   <= di[15:8];
          if (byte_en[2]) PrefetchableMemoryLimit[23:20]  <= di[23:20];
          if (byte_en[3]) PrefetchableMemoryLimit[31:24]  <= di[31:24];
        end
        4'hA:
        begin
          if (byte_en[0]) PrefetchableMemoryBase[39:32]   <= di[7:0];
          if (byte_en[1]) PrefetchableMemoryBase[47:40]   <= di[15:8];
          if (byte_en[2]) PrefetchableMemoryBase[55:48]   <= di[23:16];
          if (byte_en[3]) PrefetchableMemoryBase[63:56]   <= di[31:24];
        end
        4'hB:
        begin
          if (byte_en[0]) PrefetchableMemoryLimit[39:32]  <= di[7:0];
          if (byte_en[1]) PrefetchableMemoryLimit[47:40]  <= di[15:8];
          if (byte_en[2]) PrefetchableMemoryLimit[55:48]  <= di[23:16];
          if (byte_en[3]) PrefetchableMemoryLimit[63:56]  <= di[31:24];
        end
        4'hC:
        begin
          if (byte_en[0]) IOBase[23:16]                   <= di[7:0];
          if (byte_en[1]) IOBase[31:24]                   <= di[15:8];
          if (byte_en[2]) IOLimit[23:16]                  <= di[23:16];
          if (byte_en[3]) IOLimit[31:24]                  <= di[31:24];
        end
      endcase
  end
  
  always @(posedge clk)
  begin
    if (rd_wr_done)
      rd_wr_done <= 1'b0;
    else
      rd_wr_done <= rd_en || wr_en;
  end
endmodule

module PCIConfigurationSpace #(
  parameter DEVICE_TYPE        = 4'h0
)(
  input               clk,
  input               rst,

  input   [31:0]      di,
  output  [31:0]      do,
  input   [3:0]       byte_en,
  output              rd_wr_done,
  input   [5:0]       dwaddr,
  input               rd_en,
  input               wr_en
);
  PCIeCapabilityStructure #(
    .DEVICE_TYPE (DEVICE_TYPE))
  pcie_cap(
    .clk(clk),
    .rst(rst),
    .di(di),
    .do(do),
    .byte_en(byte_en),
    .rd_wr_done(rd_wr_done),
    .dwaddr(dwaddr[3:0]),
    .rd_en(rd_en),
    .wr_en(wr_en));
  
endmodule

module PCIeExtendedConfigurationSpace #(
)(
  input               clk,
  input               rst,

  input   [31:0]      di,
  output reg [31:0]   do = 0,
  input   [3:0]       byte_en,
  output reg          rd_wr_done = 0,
  input   [3:0]       dwaddr,
  input               rd_en,
  input               wr_en

);

/*dummy code to make the compiler happy
  always @(posedge clk)
  begin
    if (rst)
	   do <= 0;
	 else
	   do <= di;
  end
*/  
  always @(posedge clk)
  begin
    if (rd_wr_done)
      rd_wr_done <= 1'b0;
    else
      rd_wr_done <= rd_en || wr_en;
  end
  
endmodule

module PCIPowerManagementCapabilityStructure
(
  input               clk,
  input               rst,

  input   [31:0]      di,
  output reg [31:0]   do = 0,
  input   [3:0]       byte_en,
  output reg          rd_wr_done = 0,
  input   [3:0]       dwaddr,
  input               rd_en,
  input               wr_en
);
endmodule

module MSICapabilityStructure
(
  input               clk,
  input               rst,

  input   [31:0]      di,
  output reg [31:0]   do = 0,
  input   [3:0]       byte_en,
  output reg          rd_wr_done = 0,
  input   [3:0]       dwaddr,
  input               rd_en,
  input               wr_en
);
endmodule

module PCIeCapabilityStructure #(
  parameter NEXT_CAP_POINTER   = 8'h0,
  parameter DEVICE_TYPE        = 4'h0,
  parameter SLOT_IMPLEMENTED   = 1'b0,
  parameter INTERRUPT_MESSAGE_NUMBER = 5'b0,
  parameter MAX_PAYLOAD_SIZE_SUPPORTED = 3'h0,
  parameter PHANTOM_FUNCTIONS_SUPPORTED = 2'h0,
  parameter EXTENDED_TAG_FIELD_SUPPORTED = 1'b0,
  parameter ENDPOINT_LOS_ACCEPTABLE_LATENCY = 3'h0,
  parameter ENDPOINT_L1_ACCEPTABLE_LATENCY = 3'h0,
  parameter ROLE_BASED_ERROR_REPORTING = 1'b0,
  parameter CAPTURED_SLOT_POWER_LIMIT_VALUE = 8'h00,
  parameter CAPTURED_SLOT_POWER_LIMIT_SCALE = 2'b00,
  parameter FUNCTION_LEVEL_RESET_CAPABILITY = 1'b0,
  parameter MAX_LINK_SPEED = 4'h1,
  parameter MAX_LINK_WIDTH = 6'h01,
  parameter ACTIVE_STATE_POWER_MANAGEMENT = 2'b00,
  parameter L0S_EXIT_LATENCY = 3'b000,
  parameter L1_EXIT_LATENCY = 3'b000,
  parameter CLOCK_POWER_MANAGEMENT = 1'b0,
  parameter SURPRISE_DOWN_ERROR_REPORTING_CAPABLE = 1'b0,
  parameter DATA_LINK_LAYER_LINK_ACTIVE_REPORTING_CAPABLE = 1'b0,
  parameter LINK_BANDWIDTH_NOTIFICATION_CAPABILITY = 1'b0,
  parameter ASPM_OPTIONALITY_COMPLIANCE = 1'b0,
  parameter PORT_NUMBER = 8'b0
)
(
  input               clk,
  input               rst,

  input   [31:0]      di,
  output reg [31:0]   do = 0,
  input   [3:0]       byte_en,
  output reg          rd_wr_done = 0,
  input   [3:0]       dwaddr,
  input               rd_en,
  input               wr_en,
  
  output reg          correctable_error_reporting_enable,
  output reg          non_fatal_error_reporting_enable,
  output reg          fatal_error_reporting_enable,
  output reg          unsupported_request_reporting_enable,
  output reg          enable_relaxed_ordering,        
  output reg [2:0]    max_payload_size,
  output reg          extended_tag_field_enable,
  output reg          phantom_function_enable,
  output reg          aux_power_pm_enable,
  output reg          enable_no_snoop,
  output reg [2:0]    max_read_request_size,
  output reg          bridge_configuration_retry_enable_or_initiate_function_level_reset,
  output reg [1:0]    active_state_power_management,
  output reg          read_completion_boundary,
  output reg          link_disable,
  output reg          retrain_link,
  output reg          common_clock_configuration,
  output reg          extended_sync,
  output reg          enable_clock_power_management,
  output reg          hardware_autonomous_width_disable,
  output reg          link_bandwidth_management_interrupt_enable,
  output reg          link_autonomous_bandwidth_interrupt_enable,
  input               correctable_error_detected,
  input               non_fatal_error_detected,
  input               fatal_error_detected,
  input               unsupported_request_detected,
  input               aux_power_detected,
  input               transaction_pending,
  input [3:0]         current_link_speed,
  input [5:0]         negotiated_link_width,
  input               link_training,
  input               data_link_layer_link_active
);

  wire [15:0] PCIeCapabilities;
  wire [31:0] DeviceCapabilities;
  wire [15:0] DeviceControl;
  wire [15:0] DeviceStatus;
  wire [31:0] LinkCapabilities;
  wire [15:0] LinkControl;
  wire [15:0] LinkStatus;
  wire [31:0] SlotCapabilities;
  wire [15:0] SlotControl;
  wire [15:0] SlotStatus;
  wire [15:0] RootControl;
  wire [15:0] RootCapabilities;
  wire [31:0] RootStatus;
  wire [31:0] DeviceCapabilities2;
  wire [15:0] DeviceControl2;
  wire [15:0] DeviceStatus2 = 16'h0;
  wire [31:0] LinkCapabilities2;
  wire [15:0] LinkControl2;
  wire [15:0] LinkStatus2;
  wire [31:0] SlotCapabilities2 = 32'h0;
  wire [15:0] SlotControl2 = 16'h0;
  wire [15:0] SlotStatus2 = 16'h0;
  reg rw1c_correctable_error_detected;
  reg rw1c_non_fatal_error_detected;
  reg rw1c_fatal_error_detected;
  reg rw1c_unsupported_request_detected;
  reg rw1c_aux_power_detected;
  reg rw1c_transaction_pending;
  
  assign PCIeCapabilities[3:0] = 4'h2;
  assign PCIeCapabilities[7:4] = DEVICE_TYPE;
  assign PCIeCapabilities[8] = SLOT_IMPLEMENTED;
  assign PCIeCapabilities[13:9] = INTERRUPT_MESSAGE_NUMBER;
  assign PCIeCapabilities[15:14] = 2'b0;
  
  assign DeviceCapabilities[2:0] = MAX_PAYLOAD_SIZE_SUPPORTED;
  assign DeviceCapabilities[4:3] = PHANTOM_FUNCTIONS_SUPPORTED;
  assign DeviceCapabilities[5] = EXTENDED_TAG_FIELD_SUPPORTED;
  assign DeviceCapabilities[8:6] = ENDPOINT_LOS_ACCEPTABLE_LATENCY;
  assign DeviceCapabilities[11:9] = ENDPOINT_L1_ACCEPTABLE_LATENCY;
  assign DeviceCapabilities[14:12] = 3'b0;
  assign DeviceCapabilities[15] = ROLE_BASED_ERROR_REPORTING;
  assign DeviceCapabilities[25:18] = CAPTURED_SLOT_POWER_LIMIT_VALUE;
  assign DeviceCapabilities[27:26] = CAPTURED_SLOT_POWER_LIMIT_SCALE;
  assign DeviceCapabilities[28] = FUNCTION_LEVEL_RESET_CAPABILITY;
  assign DeviceCapabilities[31:29] = 3'b0;
  
  assign DeviceControl[0] = correctable_error_reporting_enable;
  assign DeviceControl[1] = non_fatal_error_reporting_enable;
  assign DeviceControl[2] = fatal_error_reporting_enable;
  assign DeviceControl[3] = unsupported_request_reporting_enable;
  assign DeviceControl[4] = enable_relaxed_ordering;
  assign DeviceControl[7:5] = max_payload_size;
  assign DeviceControl[8] = extended_tag_field_enable;
  assign DeviceControl[9] = phantom_function_enable;
  assign DeviceControl[10] = aux_power_pm_enable;
  assign DeviceControl[11] = enable_no_snoop;
  assign DeviceControl[14:12] = max_read_request_size;
  assign DeviceControl[15] = bridge_configuration_retry_enable_or_initiate_function_level_reset;
  
  assign DeviceStatus[0] = rw1c_correctable_error_detected;
  assign DeviceStatus[1] = rw1c_non_fatal_error_detected;
  assign DeviceStatus[2] = rw1c_fatal_error_detected;
  assign DeviceStatus[3] = rw1c_unsupported_request_detected;
  assign DeviceStatus[4] = rw1c_aux_power_detected;
  assign DeviceStatus[5] = rw1c_transaction_pending;
  assign DeviceStatus[15:6] = 10'b0;
  
  assign LinkCapabilities[3:0] = MAX_LINK_SPEED;
  assign LinkCapabilities[9:4] = MAX_LINK_WIDTH;
  assign LinkCapabilities[11:10] = ACTIVE_STATE_POWER_MANAGEMENT;
  assign LinkCapabilities[14:12] = L0S_EXIT_LATENCY;
  assign LinkCapabilities[17:15] = L1_EXIT_LATENCY;
  assign LinkCapabilities[18] = CLOCK_POWER_MANAGEMENT;
  assign LinkCapabilities[19] = SURPRISE_DOWN_ERROR_REPORTING_CAPABLE;
  assign LinkCapabilities[20] = DATA_LINK_LAYER_LINK_ACTIVE_REPORTING_CAPABLE;
  assign LinkCapabilities[21] = LINK_BANDWIDTH_NOTIFICATION_CAPABILITY;
  assign LinkCapabilities[22] = ASPM_OPTIONALITY_COMPLIANCE;
  assign LinkCapabilities[23] = 1'b0;
  assign LinkCapabilities[31:24] = PORT_NUMBER;
  
  always @(posedge clk)
  begin
    if (wr_en && (dwaddr[3:0] == 4'h2) && byte_en[2] && di[16])
      rw1c_correctable_error_detected <= 1'b0;
    else if (correctable_error_detected)
      rw1c_correctable_error_detected <= 1'b1;
    if (wr_en && (dwaddr[3:0] == 4'h2) && byte_en[2] && di[17])
      rw1c_non_fatal_error_detected <= 1'b0;
    else if (non_fatal_error_detected)
      rw1c_non_fatal_error_detected <= 1'b1;
    if (wr_en && (dwaddr[3:0] == 4'h2) && byte_en[2] && di[18])
      rw1c_fatal_error_detected <= 1'b0;
    else if (fatal_error_detected)
      rw1c_fatal_error_detected <= 1'b1;
    if (wr_en && (dwaddr[3:0] == 4'h2) && byte_en[2] && di[19])
      rw1c_unsupported_request_detected <= 1'b0;
    else if (unsupported_request_detected)
      rw1c_unsupported_request_detected <= 1'b1;
    if (wr_en && (dwaddr[3:0] == 4'h2) && byte_en[2] && di[20])
      rw1c_aux_power_detected <= 1'b0;
    else if (aux_power_detected)
      rw1c_aux_power_detected <= 1'b1;
    if (wr_en && (dwaddr[3:0] == 4'h2) && byte_en[2] && di[21])
      rw1c_transaction_pending <= 1'b0;
    else if (transaction_pending)
      rw1c_transaction_pending <= 1'b1;
  end
  
  always @(posedge clk)
  begin
    case (dwaddr[3:0])
      4'h2:
      begin
        if (byte_en[0])
        begin
          correctable_error_reporting_enable <= di[0];
          non_fatal_error_reporting_enable <= di[1];
          fatal_error_reporting_enable <= di[2];
          unsupported_request_reporting_enable <= di[3];
          enable_relaxed_ordering <= di[4];
          max_payload_size <= di[7:5];
        end
        if (byte_en[1])
        begin
          extended_tag_field_enable <= di[8];
          phantom_function_enable <= di[9];
          aux_power_pm_enable <= di[10];
          enable_no_snoop <= di[11];
          max_read_request_size <= di[14:12];
          bridge_configuration_retry_enable_or_initiate_function_level_reset <= di[15];
        end
      end
    endcase
  end
  
  always @(posedge clk)
  begin
    case (dwaddr[3:0])
      4'h0:
        do <= {PCIeCapabilities, NEXT_CAP_POINTER, 8'h10};
      4'h1:
        do <= DeviceCapabilities;
      4'h2:
        do <= {DeviceStatus, DeviceControl};
      4'h3:
        do <= LinkCapabilities;
      4'h4:
        do <= {LinkStatus, LinkControl};
      4'h5:
        do <= SlotCapabilities;
      4'h6:
        do <= {SlotStatus, SlotControl};
      4'h7:
        do <= {RootCapabilities, RootControl};
      4'h8:
        do <= RootStatus;
      4'h9:
        do <= DeviceCapabilities2;
      4'hA:
        do <= {DeviceStatus2, DeviceControl2};
      4'hB:
        do <= LinkCapabilities2;
      4'hC:
        do <= {LinkStatus2, LinkControl2};
      4'hD:
        do <= SlotCapabilities2;
      4'hE:
        do <= {SlotStatus2, SlotControl2};
      4'hF:
        do <= 32'b0;
    endcase
  end
  
  always @(posedge clk)
  begin
    if (rd_wr_done)
      rd_wr_done <= 1'b0;
    else
      rd_wr_done <= rd_en || wr_en;
  end
  
endmodule

